package com.smsc.headend.task.engine.executor.impl.timesync;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.SystemClock;
import com.smsc.headend.common.utils.RedisKeys;
import com.smsc.headend.common.utils.RedisUtils;
import com.smsc.headend.module.asset.dmo.ComConfigDMO;
import com.smsc.headend.module.asset.dmo.MeterConfigDMO;
import com.smsc.headend.module.asset.entity.ComEntity;
import com.smsc.headend.module.asset.entity.Meter;
import com.smsc.headend.module.common.entity.DeviceOperation;
import com.smsc.headend.module.data.dmo.DeviceSAPandKeys;
import com.smsc.headend.module.data.dto.DataItemDTO;
import com.smsc.headend.module.data.dto.TaskDataDTO;
import com.smsc.headend.module.data.enums.DictionaryType;
import com.smsc.headend.module.data.enums.OperationGroup;
import com.smsc.headend.module.protocol.cosem.util.ByteUtils;
import com.smsc.headend.module.report.entity.LogScheduleTaskDevice;
import com.smsc.headend.module.report.entity.LogSyncTimeHis;
import com.smsc.headend.module.task.dto.AtomicTask;
import com.smsc.headend.module.task.dto.Task;
import com.smsc.headend.module.task.enums.TaskType;
import com.smsc.headend.task.engine.dao.TaskDAO;
import com.smsc.headend.task.engine.exception.UDISTaskExecutorException;
import com.smsc.headend.task.engine.executor.TaskExecutor;
import com.smsc.headend.task.engine.facade.DataItemFacade;
import com.smsc.headend.task.engine.handler.impl.timesync.SetClockHandler;
import com.smsc.headend.module.task.dto.AtomicTaskState;
import com.smsc.headend.task.engine.service.ConnectorService;
import com.smsc.headend.task.engine.service.FeignAssetManagementService;
import com.smsc.headend.task.engine.service.asset.AssetService;
import com.smsc.headend.task.engine.service.report.TimeSyncReportService;
import com.smsc.headend.task.engine.service.task.LogScheduleTaskDeviceService;
import com.smsc.headend.task.engine.service.task.TaskPacketService;
import com.smsc.headend.task.engine.service.task.TaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @author liangli
 * @date 2020/10/16
 */
@Slf4j
public class SetClockExecutor implements TaskExecutor {
    @Autowired
    RedisUtils redisUtils;
    @Autowired
    FeignAssetManagementService assetManagementRemoteService;
    @Autowired
    DataItemFacade dataItemFacade;
    @Autowired
    TaskPacketService packetService;
    @Autowired
    ConnectorService connectorService;
    @Autowired
    TaskDAO taskDAO;
    @Autowired
    TimeSyncReportService timeSyncReportService;
    @Autowired
    TaskService taskService;
    @Autowired
    AssetService assetService;
    @Autowired
    LogScheduleTaskDeviceService logScheduleTaskDeviceService;


    @Override
    public AtomicTaskState execute(Task task, AtomicTask atomicTask) throws UDISTaskExecutorException {
        DeviceOperation dictionary = assetService.getDictionary(atomicTask.getOperationId(), DictionaryType.Operation.getType());
        Meter meter = getMeter(task, assetService);
        MeterConfigDMO meterConfigDMO = task.getMeterId() == null ? null : assetService.getMeterConfigById(task.getMeterId());
        ComEntity terminal = assetService.getComById(task.getComId());
        ComConfigDMO comConfigDMO = assetService.getComConfigById(task.getComId());
        List<DataItemDTO> dataItemDTOList = dataItemFacade.getDataItemsByOperation(meter, terminal, dictionary, getParameterMap(task, atomicTask), comConfigDMO.getModelId());
        DeviceSAPandKeys deviceKeys = meter != null ? assetManagementRemoteService.getMeterCommunicationKey(meter.getMeterId(), dataItemDTOList.get(0).getRoleId()) :
                assetManagementRemoteService.getTerminalCommunicationKey(terminal.getComId(), dataItemDTOList.get(0).getRoleId());

        Date targetDate = new Date();
        Map param = getParameterMap(task, atomicTask);
        if (param.containsKey("targetTv")) {
            Long targetTv = Convert.toLong(param.get("targetTv"));
            targetDate = new Date(targetTv * 1000);
        }
        log.info("setting time for device {}", DateFormatUtils.format(targetDate, "yyyy-MM-dd HH:mm:ss"));

        //对时时间
        Long syncTime = SystemClock.now() / 1000;
        taskDAO.addTaskData(task.getTaskNo(), atomicTask.getAtomicTaskNo(), "syncTime", syncTime, 300L, null);
        redisUtils.set(RedisKeys.getClockSyncTime(task.getTaskNo()), syncTime);

        Long modelId = meter != null ? meter.getModelId() : comConfigDMO.getModelId();
        byte[] packets = packetService.setClockTimePacket(dataItemDTOList.get(0), meterConfigDMO != null ? meterConfigDMO : comConfigDMO, modelId, deviceKeys, targetDate);
        log.info(ByteUtils.byteToHexString(packets));
        connectorService.sendPacket(task, atomicTask, dataItemDTOList.get(0).getRoleId(), task.getIsODR(), packets);

        return createAtomicState(SetClockHandler.class);
    }

    @Override
    public Boolean handleFailure(Task task, AtomicTask atomicTask, String message) {
        Map<String, Object> map = getParameterMap(task, atomicTask);
        List<TaskDataDTO> list = taskDAO.getAllTaskData(task.getTaskNo());
        TaskDataDTO syncDTO = null;
        TaskDataDTO timeDiffBeforeDto = null;
        if (null != list) {
            syncDTO = list.stream().filter(taskDataDTO -> "deviceTime".equalsIgnoreCase(taskDataDTO.getKey())).findFirst().orElse(null);
            timeDiffBeforeDto = list.stream().filter(taskDataDTO -> "timeDiffBefore".equalsIgnoreCase(taskDataDTO.getKey())).findFirst().orElse(null);
        }
        Long deviceTime = syncDTO == null ? null : Convert.toLong(syncDTO.getValue());
        Long timeDiffBefore = timeDiffBeforeDto == null ? null : Convert.toLong(timeDiffBeforeDto.getValue());
        Long userId = Convert.toLong(map.get("userId"));
        Long deviceId = taskService.getDeviceId(task);
        Long deviceType = taskService.getDeviceType(task);
        Long serverTime = SystemClock.now() / 1000;
        //系统同步
        if (task.getTaskType().equalsIgnoreCase(TaskType.System_Time_Sync.toString())) {
            timeSyncReportService.saveReport(deviceId, deviceType, null
                    , LogSyncTimeHis.SyncResult.SYNC_FAIL.getCode()
                    , deviceTime, serverTime, timeDiffBefore, null, userId, 0);
            logScheduleTaskDeviceService.sendMessage(task, LogScheduleTaskDevice.StatusEnum.Failed.getCode(), LogScheduleTaskDevice.StatusDescEnum.DecodeDataFailed.getCode());
        }
        //手工Set/Sync
        if (task.getTaskType().equalsIgnoreCase(TaskType.Manual_Time_Sync.toString())) {
            //人工Set
            if (OperationGroup.ManualTimeSyncWrite.getId().longValue() == atomicTask.getGroupId()) {
                timeSyncReportService.saveReport(deviceId, deviceType, null
                        , LogSyncTimeHis.SyncResult.SYNC_FAIL.getCode()
                        , deviceTime, serverTime, timeDiffBefore, null, userId, 1);
            }
            //人工Sync
            if (OperationGroup.TimeSync.getId().longValue() == atomicTask.getGroupId()) {
                timeSyncReportService.saveReport(deviceId, deviceType, null
                        , LogSyncTimeHis.SyncResult.SYNC_FAIL.getCode()
                        , deviceTime, serverTime, timeDiffBefore, null, userId, 2);
            }
        }
        return true;
    }
}
